/*
 * Copyright (c) 2025 Huawei Device, Inc. Ltd. and <马弓手>.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef MYAPPLICATION_WHITELIST_H
#define MYAPPLICATION_WHITELIST_H
#include <iostream>
#include <vector>
#include <string>
#include <regex>
#include <memory>
#include <stdexcept>

using namespace  std;

class Whitelist {
    class URLPattern {
    public:
        shared_ptr<regex> scheme;
        shared_ptr<regex> host;
        shared_ptr<int> port;
        shared_ptr<regex> path;
    private:
        string regexFromPattern(const string& pattern, bool allowWildcards) {
            const string toReplace = ".[]{}()^$?+|";
            string regexStr;
            
            for (char c : pattern) {
                if (c == '*' && allowWildcards) {
                    regexStr += ".";
                } else if (toReplace.find(c) != string::npos) {
                    regexStr += '\\';
                }
                regexStr += c;
            }
            return regexStr;
        }
    public:
        URLPattern(const string& scheme, const string& host, const string& port, const string& path) {
            // Scheme处理
            if (scheme.empty() || scheme == "*") {
                this->scheme = nullptr;
            } else {
                string schemeRegex = regexFromPattern(scheme, false);
                this->scheme = make_shared<regex>(schemeRegex, regex_constants::icase);
            }
            
            // Host处理
            if (host == "*") {
                this->host = nullptr;
            } else if (host.size() >= 2 && host.substr(0, 2) == "*.") {
                string hostRegex = "([a-z0-9.-]*\\.)?" + regexFromPattern(host.substr(2), false);
                this->host = make_shared<regex>(hostRegex, regex_constants::icase);
            } else {
                string hostRegex = regexFromPattern(host, false);
                this->host = make_shared<regex>(hostRegex, regex_constants::icase);
            }

            // Port处理
            if (port.empty() || port == "*") {
                this->port = nullptr;
            } else {
                int portNum = stoi(port);
                if (portNum < 0 || portNum > 65535) {
                    throw invalid_argument("Port must be between 0 and 65535");
                }
                this->port = make_shared<int>(portNum);
            }

            // Path处理
            if (path.empty() || path == "/*") {
                this->path = nullptr;
            } else {
                string pathRegex = regexFromPattern(path, true);
                this->path = make_shared<regex>(pathRegex);
            }
        }

        bool matches(const string& uri) const {
            size_t schemeEnd = uri.find("://");
            if (schemeEnd == string::npos) {
                return false;
            }

            string schemeStr = uri.substr(0, schemeEnd);
            string remaining = uri.substr(schemeEnd + 3);

            size_t hostStart = 0;
            size_t hostEnd = remaining.find('/');
            string hostPortStr = (hostEnd == string::npos) ? remaining : remaining.substr(0, hostEnd);

            size_t portStart = hostPortStr.find(':');
            string hostStr = (portStart == string::npos) ? hostPortStr : hostPortStr.substr(0, portStart);
            string portStr = (portStart == string::npos) ?"" : hostPortStr.substr(portStart + 1);

            string pathStr = (hostEnd == string::npos) ?"/" : remaining.substr(hostEnd);

            int portNum = -1;
            if (!portStr.empty()) {
                portNum = stoi(portStr);
            }

            // 检查各个部分的匹配
            if (scheme && !regex_match(schemeStr, *scheme)) {
                return false;
            }

            if (host && !regex_match(hostStr, *host)) {
                return false;
            }

            if (port) {
                if (portNum == -1) {
                    // 如果没有明确指定端口，使用默认端口
                    if (schemeStr == "http") portNum = 80;
                    else if (schemeStr == "https") portNum = 443;
                    else if (schemeStr == "ftp") portNum = 21;
                }
                if (portNum != *port) {
                    return false;
                }
            }
            if (path && !regex_match(pathStr, *path)) {
                return false;
            }
            return true;
        }
    };
    vector<URLPattern> whiteList;
    bool isLimitedAccess;
public:
    
    Whitelist() : isLimitedAccess(false) {}
    ~Whitelist(){}

    void addWhiteListEntry(const string& origin) {
        if (isLimitedAccess) {
            return; // 已经是无限制访问模式
        }
        
        // 无限制访问网络资源
        if (origin == "*") {
            isLimitedAccess = true;
            whiteList.clear();
            return;
        }

        // 特定的访问规则
        regex partsRegex("^((\\*|[A-Za-z-]+):(//)?)?(\\*|((\\*\\.)?[^*/:]+))?(:(\\d+))?(/.*)?");
        smatch matches;
            
        if (regex_match(origin, matches, partsRegex)) {
            string scheme = matches[2].str();
            string host = matches[4].str();
            
            // 特殊处理：file和content协议允许空主机
            if ((scheme == "file" || scheme == "content") && host.empty()) {
                host = "*";
            }
            
            string port = matches[8].str();
            string path = matches[9].str();

            if (scheme.empty()) {
                // 如果没有指定协议，默认添加http和https
                whiteList.emplace_back("http", host, port, path);
                whiteList.emplace_back("https", host, port, path);
            } else {
                whiteList.emplace_back(scheme, host, port, path);
            }
        }
    }

    bool isUrlWhiteListed(const string& uri) const {
        // 如果是无限制访问模式
        if (isLimitedAccess) {
            return true;
        }

        // 在白名单中查找匹配
        for (const auto& pattern : whiteList) {
            if (pattern.matches(uri)) {
                return true;
            }
        }
        
        return false;
    }

    // 清空白名单
    void clear() {
        whiteList.clear();
        isLimitedAccess = false;
    }

    // 获取白名单大小
    size_t size() const {
        return whiteList.size();
    }

    // 检查是否是无限制访问模式
    bool isUnlimited() const {
        return isLimitedAccess;
    }
};
#endif //MYAPPLICATION_WHITELIST_H
